// John Lake
// CSCE 441

#include <stdlib.h>
#include <math.h>
#include <vector>
#include <list>
#include <algorithm>


enum ShadingMode
{
	FLAT,
	GOURAUD,
	PHONG
};

class Color {
public:
	Color();
	Color(float _r, float _g, float _b);
	~Color();
	void clamp();
	float r, g, b;		// Color (R,G,B values)
};


/**********************************************************************************/
class Point {
	friend class Edge;
	friend class Polygon;

//protected:	// would be better coding convention to fix it, but w/e

public:
	double x, y, z;

	Point();
	Point(double _x, double _y, double _z);

	void normalize();
	Point operator-(Point& rhs);

	~Point();
};

typedef Point Normal;	// just for naming clarity; will use this structure to hold normals



/**********************************************************************************/

// outline for Edge taken from slides in class
class Edge {
	friend class Face;
	friend class EdgeList;
	friend class Polygon;

protected:
	int maxY;		// maximum y value of the line
	int minY;		// minimum y value of the line

	double xyIncr;	// = 1/XYslope
		//    will need to deal with case that line is horizontal/vertical
//	double xzIncr;	// = 1/XZslope
//	double zyIncr;	// = 1/ZYslope

	Point p1;	// hold endpoint 1
	Point p2;	// hold endpoint 2

	bool horizontal;

public:
	Edge();
	Edge(Point p1, Point p2);
	~Edge();

	// protect?
	double currentX;	// x-value of endpoint with lowest y value


	bool operator<(Edge& rhs);
	void printEdge();
};


/**********************************************************************************/


class EdgeList {
	friend class Polygon;

protected:

	int elminY;			// which scanline does this list represent?
	int elmaxY;

	//std::list<Edge> edgeList;	// the actual list of edges
	//Edge* edgeList;
	std::list<Edge> edgeList;

	void addEdge(Edge _e);	// maybe make into references; am worried about cleaning
							//    of the edge after the function call
	void printEdgeList();



public:
	EdgeList();			// remove this later
	EdgeList(Edge _e);
//	EdgeList(Edge* _e);
	~EdgeList();

	bool compareEdgeList(EdgeList first, EdgeList second);
	bool operator<(EdgeList& rhs);

};


/**********************************************************************************/

/*
class ActiveEdgeList : public EdgeList {
public:
	ActiveEdgeList(Edge _e);
	~ActiveEdgeList();

	bool operator<(ActiveEdgeList& rhs);

};
*/


/**********************************************************************************/
	
/** 
 * For this assignment, a face will always be a triangle; thus, I can specify 
 *   specific numbers of Points in a face or Edges in a face; in a more general case
 *   it would make more sense to make them vectors (or use this and split faces with
 *   more than four vertices into triangles
 */
class Face {
	friend class FaceList;
	friend class Polygon;

protected:

	Point v1, v2, v3;	// vertices of the face
//	Edge e1, e2, e3;	// will be the edges between the vertices; makes it easier to put it here

	int vNum[3];		// to hold the indices of the vertices; will use when averaging normals
						//   for Gouraud and Phong shading

	Normal vNormals[3];	// the normals of the vertices (in order)
	Normal faceNormal;	// the face's normal = (v3-v1)x(v2-v1)

//	int elminY;			// which scanline does this list represent?
//	int elmaxY;

	int minY, maxY;		// what is the minimum/maximum y value for which we worry about this face?

	Color flatColor;


	//std::list<Edge> edgeList;	// the actual list of edges
	//Edge* edgeList;
//	std::list<Edge*> edgeList;

//	void addEdge(Edge* _e);	// maybe make into references; am worried about cleaning
							//    of the edge after the function call
//	void printEdgeList();



public:
	Face();
	Face(Point v1, Point v2, Point v3, int vNum1, int vNum2, int vNum3);
	~Face();

	float getZ(float x, float y);
	Color getColor(float x, float y, ShadingMode sm);
	float interpolate(float targetX, float targetY, float p1, float p2, float p3);
	float interpolate2(float targetX, float x1, float x2, float p1, float p2);
	
//	bool compareEdgeList(EdgeList first, EdgeList second);
//	bool operator<(Face& rhs);

};



/**********************************************************************************/


class FaceList {
	friend class Polygon;

protected:

	int flminY;			// which scanline does this list represent? - is this needed?
	int flmaxY;

	//std::list<Edge> edgeList;	// the actual list of edges
	//Edge* edgeList;

	// faceList could be a vector, but am keeping in similarity with EdgeList (which needs to be able to sort)
	std::list<Face> faceList;	

	void addFace(Face _f);

//	void printFaceList();



public:
	FaceList();
//	FaceList(Face _f);
	//FaceList(Edge* _e);
	~FaceList();

	void clear();

	bool compareFaceList(FaceList first, FaceList second);
	bool operator<(FaceList& rhs);

};



/**********************************************************************************/

class Polygon {
private:
	// don't need this?
	int windowWidth, windowHeight;	// window height and width; set in constructor; 
	// determines size of AE Table
	
	
	//Color color;	// Color is now a function of lighting

	std::vector<Point> vertices;
	std::vector<Normal> vertexNormals;
	std::list<FaceList> afTable;	// list of all faces	// originally was a list - any reason?
	FaceList faces;
	FaceList afList;				// list of active faces (for use when drawing)



public:
	Polygon();
	Polygon(int _windowWidth, int _windowHeight);
	~Polygon();

//	std::list<Point*> vTable;	// list of all vertices

	void addVertex(Point _p);	
	void addFace(Face _f);		// in case I need to randomly add a face; shouldn't be used
	void addFace(int x, int y, int z);	// x,y,z are the vertex numbers of the face

	void updatePolygon();		// to average the vertices

	void drawPolygon(ShadingMode sm);	// will be the one to call setFrameBuffer();
	void printEdgeTable();
//	Polygon* clipPolygon(Polygon* original, Point _p1, Point _p2);	// clipping is not needed for this assignment
};


/**********************************************************************************/